﻿using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.Reporting.WinForms;

public class PrintHelper : IDisposable {

    private IList<Stream> _streams;
    private int _currentPageIndex;
    private string _reportName, _deviceInfo, _tempFolderPath, _printDocumentName;
    private List<ReportDataSource> _datasources;

    public List<ReportDataSource> ReportDataSources {
        get { return this._datasources; }
        set { this._datasources = value; }
    }

    public PrintHelper(string reportName, string printDocumentName, string deviceInfo = null) {
        string defaultDeviceInfo =
          "<DeviceInfo>" +
          "  <OutputFormat>EMF</OutputFormat>" +
          "  <PageWidth>21cm</PageWidth>" +
          "  <PageHeight>29.7cm</PageHeight>" +
          "  <MarginTop>0cm</MarginTop>" +
          "  <MarginLeft>0cm</MarginLeft>" +
          "  <MarginRight>0cm</MarginRight>" +
          "  <MarginBottom>0cm</MarginBottom>" +
          "</DeviceInfo>";

        this._datasources = new List<ReportDataSource>();
        this._reportName = reportName;
        this._deviceInfo = string.IsNullOrEmpty(deviceInfo) ? defaultDeviceInfo : deviceInfo;
        this._tempFolderPath = Environment.GetEnvironmentVariable("TEMP");
        this._printDocumentName = printDocumentName;
    }

    private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek) {
        string tempFile = string.Format(@"{0}/{1}.{2}", this._tempFolderPath, name, fileNameExtension);
        Stream stream = new FileStream(tempFile, FileMode.Create);
        this._streams.Add(stream);
        return stream;
    }

    private void Export(LocalReport report) {
        Warning[] warnings;
        this._streams = new List<Stream>();
        report.Render("Image", this._deviceInfo, CreateStream, out warnings);

        foreach (Stream stream in this._streams) {
            stream.Position = 0;
        }
    }

    private void PrintPage(object sender, PrintPageEventArgs ev) {
        Metafile pageImage = new Metafile(this._streams[this._currentPageIndex]);
        ev.Graphics.DrawImage(pageImage, ev.PageBounds);

        this._currentPageIndex++;
        ev.HasMorePages = (this._currentPageIndex < this._streams.Count);
    }

    private void Print() {
        if (_streams == null || _streams.Count == 0) {
            return;
        }

        using (PrintDialog pd = new PrintDialog()) {
            using (PrintDocument printDoc = new PrintDocument()) {
                double pageWidth = 0, pageHeight = 0;
                if (Regex.IsMatch(this._deviceInfo, @"<PageWidth>(?<width>\d+.?\d+)cm</PageWidth>")) {
                    pageWidth = double.Parse(Regex.Match(this._deviceInfo, @"<PageWidth>(?<width>\d+.?\d+)cm</PageWidth>").Groups["width"].Value);
                }
                if (Regex.IsMatch(this._deviceInfo, @"<PageHeight>(?<height>\d+.?\d+)cm</PageHeight>")) {
                    pageHeight = double.Parse(Regex.Match(this._deviceInfo, @"<PageHeight>(?<height>\d+.?\d+)cm</PageHeight>").Groups["height"].Value);
                }

                PaperSize ps = new PaperSize("current page", (int)(pageWidth / 2.54 * 100), (int)(pageHeight / 2.54 * 100));
                ps.RawKind = 119; // Greater than 118 indicate a custom paper size
                printDoc.PrinterSettings.DefaultPageSettings.PaperSize = ps;
                printDoc.DefaultPageSettings.PaperSize = ps;
                printDoc.DocumentName = this._printDocumentName;
                printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
                pd.AllowPrintToFile = false;
                pd.AllowSelection = true;
                pd.AllowSomePages = true;
                pd.Document = printDoc;
                if (pd.ShowDialog() == DialogResult.OK) {
                    printDoc.Print();
                }
            }
        }
    }

    public void Run() {
        LocalReport report = new LocalReport();
        report.ReportPath = Path.Combine(Application.StartupPath, "Reports", this._reportName);
        foreach (ReportDataSource rds in this._datasources) {
            report.DataSources.Add(rds);
        }

        this.Export(report);
        this._currentPageIndex = 0;
        this.Print();
    }

    public void Dispose() {
        if (this._streams != null) {
            foreach (Stream stream in this._streams) {
                stream.Close();
            }
            this._streams = null;
        }
    }

}